#include "proxy_kpabe.h"
#include "kpabe_KPAbe.h"
#include "string.h"
#include <stdlib.h>
#include "pbc_test.h"
#include <stdio.h>
#include <iostream>
using namespace std;
extern kpabe_pub_t* pub;
extern int beta;

kpabe_prv_t* transformKey; 
element_t userDecKey, userMask;

kpabe_pub_t* convertToPubKey(unsigned char* pubBytes, int pubLen){
	GByteArray* buf = convertToByteArray(pubBytes, pubLen);
	return kpabe_pub_unserialize(buf, 1);
}

kpabe_prv_t* convertToPrvKey(unsigned char* prvBytes, int prvLen){
	return kpabe_prv_unserialize(pub, convertToByteArray(prvBytes, prvLen), 1);
}

int toByteArray(GByteArray* buf, unsigned char** byteArray, int *len){
	*len = buf->len;
	*byteArray = (unsigned char*)malloc((*len)*sizeof(char));
	for (int i=0; i<(*len); i++)
		(*byteArray)[i] = buf->data[i];
	return 0;
}

int initUser_simple(unsigned char* pubBytes, int pubLen, unsigned char* prvBytes, int privLen, unsigned char*
dec, int decLen){
	pub = convertToPubKey(pubBytes, pubLen);
	initDiscreteLogMap(5000);
	transformKey = convertToPrvKey(prvBytes, privLen);
	element_init_Zr(userDecKey, pub->p);
	element_from_bytes(userDecKey, dec);
	return 0;
}

JNIEXPORT void JNICALL Java_kpabe_KPAbe_initUser_1simple
  (JNIEnv *env, jobject obj, jbyteArray pubB, jbyteArray tkB, jbyteArray decB){
	int pl = env->GetArrayLength(pubB);
	int tkl = env->GetArrayLength(tkB);
	int dl = env->GetArrayLength(decB);
	
	unsigned char *pubBytes = (unsigned char*)malloc(pl*sizeof(char));
	unsigned char *prvBytes = (unsigned char*)malloc(tkl*sizeof(char));
	unsigned char *decBytes = (unsigned char*)malloc(dl*sizeof(char));
	
	env->GetByteArrayRegion(pubB,0,pl,(jbyte*)pubBytes);
	env->GetByteArrayRegion(tkB,0,tkl,(jbyte*)prvBytes);
	env->GetByteArrayRegion(decB,0,dl,(jbyte*)decBytes);

	initUser_simple(pubBytes,pl,prvBytes,tkl,decBytes,dl);
}


int initUser_slidingWindow(unsigned char* pubBytes, int pubLen, unsigned char* prvBytes, int privLen, unsigned char*
dec, int decLen, unsigned char* mask, int maskLen, int bt){
	pub = convertToPubKey(pubBytes, pubLen);
	initDiscreteLogMap(5000);
	transformKey = convertToPrvKey(prvBytes, privLen);
	element_init_Zr(userDecKey, pub->p);
	element_init_Zr(userMask, pub->p);
	element_from_bytes(userDecKey, dec);
	element_from_bytes(userMask, mask);
	beta = bt;
	return 0;
}

JNIEXPORT void JNICALL Java_kpabe_KPAbe_initUser_1slidingWindow
  (JNIEnv *env, jobject obj, jbyteArray pubB, jbyteArray prvB, jbyteArray decB, jbyteArray
maskB, jint beta){
	int pl = env->GetArrayLength(pubB);
	int tkl = env->GetArrayLength(prvB);
	int dl = env->GetArrayLength(decB);
	int ml = env->GetArrayLength(maskB);
	
	unsigned char *pubBytes = (unsigned char*)malloc(pl*sizeof(char));
	unsigned char *prvBytes = (unsigned char*)malloc(tkl*sizeof(char));
	unsigned char *decBytes = (unsigned char*)malloc(dl*sizeof(char));
	unsigned char *maskBytes = (unsigned char*)malloc(ml*sizeof(char));	

	env->GetByteArrayRegion(pubB,0,pl,(jbyte*)pubBytes);
	env->GetByteArrayRegion(prvB,0,tkl,(jbyte*)prvBytes);
	env->GetByteArrayRegion(decB,0,dl,(jbyte*)decBytes);
	env->GetByteArrayRegion(maskB,0,ml,(jbyte*)maskBytes);

	initUser_slidingWindow(pubBytes,pl,prvBytes,tkl,decBytes,dl,maskBytes,ml,beta);

}

//received the ciphertext, need to umask the e(g,g)^{y.s} first
//then perform discrete log.
//the input cipher has been transformed
int decrypt_simple(unsigned char* cipher, unsigned char* originalCipher){
	element_t userCipher, val;
	element_init_GT(userCipher, pub->p);
	element_init_GT(val, pub->p);
	element_from_bytes(userCipher, cipher);
	element_from_bytes(val, originalCipher);	

	element_t decVal;
	kpabe_dec_proxy(pub,userCipher,userDecKey,val,decVal);
	element_printf("Dec = %B\n",decVal);
	return toPlaintext_simple(decVal); 		
}

JNIEXPORT jint JNICALL Java_kpabe_KPAbe_decrypt_1simple
  (JNIEnv *env, jobject obj, jbyteArray cipher, jbyteArray originalCipher){
	int cl = env->GetArrayLength(cipher);
	int ocl = env->GetArrayLength(originalCipher);
	unsigned char *cipherBytes = (unsigned char*)malloc(cl*sizeof(char));
	unsigned char *originalCipherBytes = (unsigned char*)malloc(ocl*sizeof(char));
	
	env->GetByteArrayRegion(cipher,0,cl,(jbyte*)cipherBytes);
	env->GetByteArrayRegion(originalCipher,0,ocl,(jbyte*)originalCipherBytes);
	cout << "dec start = "<<pbc_get_time()<<endl;
	int val = decrypt_simple(cipherBytes, originalCipherBytes);
	cout << "dec end = "<<pbc_get_time() << endl;
	return val;
}

//compute the unmask value, before doing the same as decrypt_simple
int decrypt_slidingWindow(unsigned char* cipher, unsigned char* originalCipher, int windowIndex){
	element_t userCipher, val;
	element_init_GT(userCipher, pub->p);
	element_init_GT(val, pub->p);
	element_from_bytes(userCipher, cipher);
	element_from_bytes(val, originalCipher);	

	element_t decVal, base2, iv, exp, tmp;
	element_init_Zr(base2, pub->p);
	element_init_Zr(exp, pub->p);
	element_init_Zr(tmp, pub->p);
	element_init_Zr(iv, pub->p);	
	
	element_set_si(base2,2);
	element_set_si(iv,windowIndex);
	element_pow_zn(tmp, base2, iv);
	element_mul(exp,tmp,userMask);

	kpabe_dec_proxy(pub,userCipher,userDecKey,val, decVal);
	return toPlaintext_slidingWindow(decVal,exp); 		
}

JNIEXPORT jint JNICALL Java_kpabe_KPAbe_decrypt_1slidingWindow
  (JNIEnv *env, jobject obj, jbyteArray cipher, jbyteArray originalCipher, jint windowIndex){
	int cl = env->GetArrayLength(cipher);
	int ocl = env->GetArrayLength(originalCipher);
	unsigned char *cipherBytes = (unsigned char*)malloc(cl*sizeof(char));
	unsigned char *originalCipherBytes = (unsigned char*)malloc(ocl*sizeof(char));
	
	env->GetByteArrayRegion(cipher,0,cl,(jbyte*)cipherBytes);
	env->GetByteArrayRegion(originalCipher,0,ocl,(jbyte*)originalCipherBytes);
	return decrypt_slidingWindow(cipherBytes, originalCipherBytes,windowIndex);

}

